#include <Lucnx/printk.h>
#include <Lucnx/syscall.h>
#include <Lucnx/string.h>
#include <Lucnx/types.h>
#include <Lucnx/task.h>
#include <Lucnx/console.h>
#include <Lucnx/device.h>

static char *Lucnx_logo[] = 
{
    "  xx       xx      xx     xxxxxxx    xxxx      xx   xx     xx            \n\0",
    "  xx       xx      xx    xx    xxx   xx xx     xx    xx   xx             \n\0",
    "  xx       xx      xx   xx           xx  xx    xx     xx xx              \n\0",
    "  xx       xx      xx   xx           xx   xx   xx      xxx               \n\0",
    "  xx       xx      xx   xx           xx    xx  xx     xx xx              \n\0",
    "  xx        xx    xx     xx    xxx   xx     xx xx    xx   xx             \n\0",
    "  xxxxxxx    xxxxxx       xxxxxxx    xx      xxxx   xx     xx          \n\n\0"
};

static char cmd[128];
static char *argv[8];
static int argc;

void builtin_clear()
{
    clear();
}

void builtin_ps()
{
    ps();
}

void builtin_logo()
{
    for (size_t i = 0; i < 7; i++)
    {
        printk(Lucnx_logo[i]);
    }
}

void builtin_ld()
{
    list_devices();
}

void builtin_console()
{
    if (argc > 2)
    {
        printk("ush: args too many!\n");
        return;
    }

    int dev;
    if (!strcmp(argv[1], "kernel"))
    {
        dev = 0;
    }
    else if(!strcmp(argv[1], "user"))
    {
        dev = 1;
    }
    else
    {
        printk("ush: bad arg\n");
    }
    
    console_switch(dev);
}

void builtin_pcc()
{
    char *current_console = get_current_console() ? "user" : "kernel";
    printk("%s\n", current_console);
}

void builtin_info()
{
    printk("Lucnx v0.01 by Lscn\n");

    char buf[13];

    sysinfo(INFO_NR_CPU, buf);
    printk("cpu: %s\n", buf);

    sysinfo(INFO_NR_DISK_COUNT, buf);
    printk("disk count: %d\n", *((u8*)buf));

    sysinfo(INFO_NR_MEM_SIZE, buf);
    printk("memory size: %dMB [%dB]\n", *((u32*)((u32)buf)) / 1024 / 1024, *((u32*)((u32)buf)));
}

void builtin_help()
{
    printk("builtin commands:\n");
    printk("\tps: print information for all tasks\n");
    printk("\tinfo: print computer Information\n");
    printk("\tclear: clear screen\n");
    printk("\tconsole: switch console 'kernel' or 'user'\n");
    printk("\tpcc: print current console name\n");
    printk("\tld: print all devices\n");
}

static void command_parse(char *command)
{
    char c;
    char *ptr;
    bool is_arg = false;

    argc = 0;
    memset(argv, 0, sizeof(argv));

    size_t length = strlen(command);
    int nr = 0;
    while (nr < length)
    {
        c = command[nr];
        ptr = command + nr;

        if (c != '\0' && c != ' ' && is_arg == false)
        {
            is_arg = true;
            argv[argc] = ptr;
            argc++;
        }
        else if (is_arg == true && c == ' ')
        {
            is_arg = false;
            *ptr = '\0';
        }

        nr++;
    }
}

static void command_execute(char *command)
{
    command_parse(command);
    char *_command = argv[0]; // 这个 command 是不含参数的命令名

    if (!strcmp(_command, "clear"))
    {
        builtin_clear();
    }
    else if (!strcmp(_command, "info"))
    {
        builtin_info();
    }
    else if (!strcmp(_command, "ps"))
    {
        builtin_ps();
    }
    else if (!strcmp(_command, "help"))
    {
        builtin_help();
    }
    else if(!strcmp(_command, "console"))
    {
        builtin_console();
    }
    else if (!strcmp(_command, "pcc"))
    {
        builtin_pcc();
    }
    else if (!strcmp(_command, "logo"))
    {
        builtin_logo();
    }
    else if (!strcmp(_command, "ld"))
    {
        builtin_ld();
    }
    else
    {
        printk("ush: '%s' isn't a command\n", cmd);
        printk("use 'help' command show help\n");
    }
}

inline static void print_prompt()
{
    printk("ush@lscn# ");
}

static void get_command()
{
    memset(cmd, 0, 128);
    char *ptr = cmd;

    int nr;
    while (true)
    {
        read(stdin, ptr, 1);
        switch (*ptr)
        {
        case '\r':
        case '\n':
            *ptr = '\0';
            putchk('\n');
            return;
        case '\b':
            if (ptr != cmd)
            {
                ptr--;
                putchk('\b');
            }
            break;
            
        default:
            if (ptr - cmd < 128)
            {
                putchk(*ptr++);
            }
            break;
        }
    }
}

void ush_main()
{
    clear();
    builtin_logo();

    while (true)
    {
        print_prompt();
        get_command();

        if (cmd[0] == '\0')
        {
            continue;
        }

        command_execute(cmd);
    }
}